#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import json
import os
import re
import subprocess
import sys
import time
import xml.dom.minidom

import requests


class WebchatLogin(object):
    """ webchat login """

    def __init__(self):
        self.session = requests.session()
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) '
                          'Chrome/65.0.3325.181 Safari/537.36 ',
        }
        self.qr_image_path = os.path.split(os.path.realpath(__file__))[0] + os.sep + 'webWeixinQr.jpg'
        self.uuid = ''
        self.tip = 0
        self.base_uri = ''
        self.redirect_uri = ''
        self.skey = ''
        self.wxsid = ''
        self.wxuin = ''
        self.pass_ticket = ''
        self.device_id = 'e000000000000000'
        self.base_request = {}
        self.contact_list = {}
        self.my = []
        self.sync_key = ''

    def get_uuid(self):
        url = 'https://login.wx.qq.com/jslogin'
        params = {
            'appid': 'wx782c26e4c19acffb',
            'redirect_uri': 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage',
            'fun': 'new',
            'lang': 'zh_CN',
            '_': int(time.time() * 1000)  # timestamp
        }
        response = self.session.get(url, params=params)
        target = response.content.decode('utf-8')
        pattern = r'window.QRLogin.code = (\d+); window.QRLogin.uuid = "(\S+?)"'
        ob = re.search(pattern, target)  # 正则提取uuid
        code = ob.group(1)
        self.uuid = ob.group(2)
        if code == '200':  # 判断请求是否成功
            return True
        return False

    def show_qr_image(self):
        url = 'https://login.weixin.qq.com/qrcode/' + self.uuid
        response = self.session.get(url)
        self.tip = 1
        with open(self.qr_image_path, 'wb') as f:
            f.write(response.content)
            f.close()
        # open qr
        if sys.platform.find('drawin') >= 0:
            subprocess.call(['open', self.qr_image_path])  # ios
        elif sys.platform.find('linux') >= 0:
            subprocess.call(['xdg-open', self.qr_image_path])  # linux
        else:
            os.startfile(self.qr_image_path)  # windows
        print('请使用微信扫描二维码登录')

    def check_login(self):
        url = 'https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?uuid=%s&tip=%s&_=%s' % (
            self.uuid, self.tip, int(time.time() * 1000))
        response = self.session.get(url)
        target = response.content.decode('utf-8')
        pattern = r'window.code=(\d+);'
        ob = re.search(pattern, target)
        code = ob.group(1)
        if code == '201':  # 已扫描
            print('成功扫描,请在手机上点击确认登录')
            self.tip = 0
        elif code == '200':  # 已登录
            print('正在登录中...')
            regex = r'window.redirect_uri="(\S+?)";'
            ob = re.search(regex, target)
            self.redirect_uri = ob.group(1) + '&fun=new'
            self.base_uri = self.redirect_uri[:self.redirect_uri.rfind('/')]
        elif code == '408':  # 超时
            pass
        return code

    def login(self):
        response = self.session.get(self.redirect_uri, verify=False)
        data = response.content.decode('utf-8')
        doc = xml.dom.minidom.parseString(data)
        root = doc.documentElement
        # 提取响应中的参数
        for node in root.childNodes:
            if node.nodeName == 'skey':
                self.skey = node.childNodes[0].data
            elif node.nodeName == 'wxsid':
                self.wxsid = node.childNodes[0].data
            elif node.nodeName == 'wxuin':
                self.wxuin = node.childNodes[0].data
            elif node.nodeName == 'pass_ticket':
                self.pass_ticket = node.childNodes[0].data
        if not all((self.skey, self.wxsid, self.wxuin, self.pass_ticket)):
            return False
        self.base_request = {
            'Uin': int(self.wxuin),
            'Sid': self.wxsid,
            'Skey': self.skey,
            'DeviceID': self.device_id,
        }
        return True

    def webwxinit(self):
        url = self.base_uri + '/webwxinit?pass_ticket=%s&skey=%s&r=%s' % (
            self.pass_ticket, self.skey, int(time.time() * 1000))
        params = {
            'BaseRequest': self.base_request
        }
        header = self.headers
        header['ContentType'] = 'application/json; charset=UTF-8'
        response = self.session.post(url, data=json.dumps(params), headers=header, verify=False)
        data = response.content.decode('utf-8')
        print(data)
        dic = json.loads(data)
        self.contact_list = dic['ContactList']
        self.my = dic['User']
        sync_key_list = []
        for item in dic['SyncKey']['List']:
            sync_key_list.append('%s_%s' % (item['Key'], item['Val']))
        self.sync_key = '|'.join(sync_key_list)
        err_msg = dic['BaseResponse']['ErrMsg']
        ret = dic['BaseResponse']['Ret']
        if ret != 0:
            return False
        return True

    def webwxgetcontact(self):
        url = self.base_uri + '/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s' % (
            self.pass_ticket, self.skey, int(time.time()))
        header = self.headers
        header['ContentType'] = 'application/json; charset=UTF-8'
        response = self.session.get(url, headers=header, verify=False)
        data = response.content.decode('utf-8')
        print(data)
        dic = json.loads(data)
        member_list = dic['MemberList']
        # 倒序遍历,不然删除的时候出问题..
        special_users = ["newsapp", "fmessage", "filehelper", "weibo", "qqmail", "tmessage", "qmessage", "qqsync",
                         "floatbottle", "lbsapp", "shakeapp", "medianote", "qqfriend", "readerapp", "blogapp",
                         "facebookapp", "masssendapp",
                         "meishiapp", "feedsapp", "voip", "blogappweixin", "weixin", "brandsessionholder",
                         "weixinreminder", "wxid_novlwrv3lqwv11", "gh_22b87fa7cb3c", "officialaccounts",
                         "notification_messages", "wxitil", "userexperience_alarm"]
        for i in range(len(member_list) - 1, -1, -1):
            member = member_list[i]
            if member['VerifyFlag'] & 8 != 0:  # 公众号/服务号
                member_list.remove(member)
            elif member['UserName'] in special_users:  # 特殊账号
                member_list.remove(member)
            elif member['UserName'].find('@@') != -1:  # 群聊
                member_list.remove(member)
            elif member['UserName'] == self.my['UserName']:  # 自己
                member_list.remove(member)
        return member_list

    def main(self):
        if not self.get_uuid():
            print('获取uuid失败')
            return
        self.show_qr_image()
        time.sleep(1)
        while self.check_login() != '200':
            pass
        os.remove(self.qr_image_path)
        if not self.login():
            print('登录失败')
            return
        if not self.webwxinit():
            print('初始化失败')
            return
        member_list = self.webwxgetcontact()
        print('通讯录共%s位好友' % len(member_list))
        for member in member_list:
            sex = '未知' if member['Sex'] == 0 else '男' if member['Sex'] == 1 else '女'
            print('昵称：%s, 性别：%s, 备注：%s, 签名：%s' % (member['NickName'], sex, member['RemarkName'], member['Signature']))


if __name__ == '__main__':
    print('开始')
    wx = WebchatLogin()
    wx.main()
